package cn.geekyspace.scanhive.infrastructure.plc;

import cn.geekyspace.scanhive.infrastructure.ui.UILogger;
import com.fazecast.jSerialComm.SerialPort;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * PLC串口管理
 */
@Data
@Slf4j
@Component
@RequiredArgsConstructor
public class PLCSerialPortManager {

    private SerialPort connectionPort;   // 连接的串口

    /**
     * 串口参数配置
     */
    private static final int DEFAULT_BAUD_RATE = 9600;                                       // 波特率
    private static final int DATA_BITS = 8;                                                  // 数据位
    private static final int STOP_BITS = SerialPort.ONE_STOP_BIT;                            // 停止位
    private static final int PARITY = SerialPort.NO_PARITY;                                  // 校验位 (0=无校验)
    private static final int READ_TIMEOUT = 1000;                                            // 读取超时时间 (ms)

    // 注入UI组件
    private final UILogger uiLogger;

    // 注入PLC数据接收处理器
    private final PLCDataReceiver plcDataReceiver;

    /**
     * 获取可用串口列表（格式：COM1 #描述信息）
     */
    public List<String> getAvailableSerialPorts() {
        return Arrays.stream(SerialPort.getCommPorts())
                .map(port -> {
                    String name = port.getSystemPortName();         // 串口名称：COM1
                    String desc = port.getDescriptivePortName();    // 串口描述：Prolific USB-to-Serial Comm Port (COM1)
                    // 去除描述中的串口名称部分，避免重复显示
                    desc = desc.replace("(" + name + ")", "").trim();
                    // 返回最终格式：COM端口号 # 描述信息
                    return name + " #" + desc;
                }).toList();
    }

    /**
     * 根据串口名称建立连接
     * @param portName 串口名称（如 "COM3" 或 "/dev/ttyUSB0"）
     * @return 是否连接成功
     */
    public boolean openPort(String portName) {
        // 查找指定名称的串口
        SerialPort targetPort = findPortByName(portName);
        if (targetPort == null) {
            uiLogger.error("PLC 连接串口 " + portName + " 不存在！");
            return false;
        }

        // 尝试打开串口
        if (!targetPort.openPort()) {
            uiLogger.error("PLC 连接串口 " + portName + " 失败，可能被占用或权限不足！");
            return false;
        }

        try {
            // 设置串口通信参数
            targetPort.setComPortParameters(DEFAULT_BAUD_RATE, DATA_BITS, STOP_BITS, PARITY);
            targetPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_BLOCKING, READ_TIMEOUT, 0);

            // 尝试打开串口
            if (targetPort.isOpen()) {
                // 记录当前连接的串口
                connectionPort = targetPort;
                uiLogger.success("PLC连接成功: 串口号 = " + connectionPort.getSystemPortName() + ", 串口描述 = " + connectionPort.getDescriptivePortName());

                // ====================== 启动PLC数据接收线程 ======================
                plcDataReceiver.startReceivingData(targetPort);
                return true;
            }
        } catch (Exception e) {
            uiLogger.error("PLC 连接串口 " + portName + " 时发生错误：" + e.getMessage());
            return false;
        }

        // 关闭串口以清理资源
        targetPort.closePort();
        uiLogger.error("PLC 连接串口 " + portName + " 失败！");
        return false;
    }

    /**
     * 关闭当前连接的串口
     * @return true 如果关闭成功；false 如果没有打开的串口或关闭失败
     */
    public boolean closePort() {
        if (connectionPort != null && connectionPort.isOpen()) {
            boolean isClosed = connectionPort.closePort();
            if (isClosed) {
                uiLogger.warn("PLC断开连接: 串口号 = " + connectionPort.getSystemPortName() + ", 串口描述 = " + connectionPort.getDescriptivePortName());
                connectionPort = null;

                // 停止数据接收
                plcDataReceiver.stopReceivingData();
                return true;
            } else {
                log.warn("PLC串口关闭失败！");
                return false;
            }
        }
        return false;
    }

    /**
     * 根据串口名称查找 SerialPort 对象
     * @param portName 串口名称
     * @return 匹配的 SerialPort 对象，找不到则返回 null
     */
    private SerialPort findPortByName(String portName) {
        return Arrays.stream(SerialPort.getCommPorts())
                .filter(port -> port.getSystemPortName().equalsIgnoreCase(portName)
                        || port.getDescriptivePortName().equalsIgnoreCase(portName))
                .findFirst()
                .orElse(null);
    }

}
